## Задача 1. Объявите в программе базовый класс `Animal` (животное), объекты которого можно создать командой:

```python
an = Animal(name, old)
```

где

`name` - название животного (строка); 

`old` - возраст животного (целое число). 

Такие же локальные атрибуты (`name и old`) должны создаваться в объектах класса.

Далее, объявите дочерний класс (от базового `Animal`) с именем `Cat` (кошки), объекты которого создаются командой:

```python
cat = Cat(name, old, color, weight)
```
где 

`name, old` - те же самые параметры, что и в базовом классе; 

`color` - цвет кошки (строка); 

`weight` - вес кошки (любое положительное число).



В объектах класса `Cat` должны автоматически формироваться локальные атрибуты: `name, old, color, weight`. 

Формирование атрибутов `name, old` должен выполнять инициализатор базового класса. 

По аналогии объявите еще один дочерний класс `Dog` (собака), объекты которого создаются командой:

```python
dog = Dog(name, old, breed, size)
```

здесь `name, old` - те же самые параметры, что и в базовом классе; 

`breed` - порода собаки (строка); 

`size` - кортеж в формате (height, length) высота и длина - числа.

В объектах класса `Dog` по аналогии должны формироваться локальные атрибуты: 
`name, old, breed, size`. 

За формирование атрибутов `name, old` отвечает инициализатор базового класса. 

Наконец, в классах `Cat` и `Dog` объявите метод:

`get_info()` - для получения информации о животном.

Этот метод должен возвращать строку в формате:

`"name: old, <остальные параметры через запятую>"`

Например, для следующего объекта класса Cat:

```python
cat = Cat('кот', 4, 'black', 2.25)
метод get_info должен вернуть строку:
```

> "кот: 4, black, 2.25"

## Задача 2. Вы разрабатываете программу для интернет-магазина. 

В этом магазине могут быть как реальные (физические) товары, так и электронные.

Для этих двух групп, очевидно, нужен разный набор атрибутов:

### для реальных физических товаров: 

> `id, name, price, weight, dims`

где

* `id` - идентификатор товара (целое число); 
* `name` - наименование товара (строка); 
* `price` - цена товара (вещественное число);
* `weight` - вес товара (вещественное число); 
* `dims = (lenght, width, depth)` - длина, ширина, глубина - габариты товара (вещественные числа);


### для электронных товаров: 

> `id, name, price, memory, frm`

где
* `id` - идентификатор товара (целое число); 
* `name` - наименование товара (строка); 
* `price` - цена товара (вещественное число); 
* `memory` - занимаемый размер (в байтах - целое число); 
* `frm` - формат данных (строка: pdf, docx и т.п.)

Так как все товары могут идти вперемешку, то мы хотим, чтобы в каждом объекте (для товара) присутствовали все атрибуты:

> `id, name, price, weight, dims, memory, frm`

с начальными значениями `None`.

А уже, затем, нужным из них будут присвоены конкретные данные.

Для реализации этой логики объявите в программе базовый класс с именем `Thing` (вещь, предмет), объекты которого могут создаваться командой:

> `th = Thing(name, price)`

А атрибут `id` должен формироваться автоматически и быть уникальным для каждого товара (например, можно для каждого нового объекта увеличивать на единицу).

В объектах класса `Thing` должен формироваться полный набор локальных атрибутов (`id, name, price, weight, dims, memory, frm`) со значением `None`, кроме атрибутов: `id, name, price`.

Далее, нужно объявить два дочерних класса:

`Table` - для столов;
`ElBook` - для электронных книг.

Объекты этих классов должны создаваться командами:

```python
table = Table(name, price, weight, dims)
book = ElBook(name, price, memory, frm)
```

Причем, атрибуты `name, price` (а также `id`) следует инициализировать в базовом классе, т.к. они общие для всех товаров.

Остальные атрибуты должны либо принимать значение `None`, если не используются, либо инициализироваться конкретными значениями уже в дочерних классах.

Наконец, в базовом классе `Thing` объявите метод:

`get_data()` - для получения кортежа в формате (`id, name, price, weight, dims, memory, frm`)

### Пример использования классов:

```python
table = Table("Круглый", 1024, 812.55, (700, 750, 700))
book = ElBook("Python ООП", 2000, 2048, 'pdf')
print(*table.get_data())
print(*book.get_data())
```

## Задача 3. Объявите в программе класс с именем Singleton

который бы позволял создавать только один экземпляр (все последующие экземпляры должны ссылаться на первый). 

Затем, объявите еще один класс с именем:

> Game

который бы наследовался от класса `Singleton`. 

Объекты класса `Game` должны создаваться командой:
```python
game = Game(name)
```
где `name` - название игры (строка).

В каждом объекте класса `Game` должен создаваться атрибут `name` с соответствующим содержимым.

Убедитесь, что атрибут `name` принимает значение первого созданного объекта (если это не так, то поправьте инициализатор дочернего класса, чтобы это условие выполнялось).


## Задача 4.  Объявите класс Furniture (мебель), объекты которого создаются командой:

```python
f = Furniture(name, weight)
```
где 

`name` - название предмета (строка);
`weight` - вес предмета (целое или вещественное число).

В каждом объекте класса Furniture должны создаваться защищенные локальные атрибуты с именами `_name и _weight`. 

В самом классе `Furniture` нужно объявить приватные методы:

__verify_name() - для проверки корректности имени;
__verify_weight() - для проверки корректности веса.

Метод `__verify_name()` проверяет, что имя должно быть строкой, если это не так, то генерируется исключение командой:

```python
raise TypeError('название должно быть строкой')
```
Метод `__verify_weight()` проверяет, что вес должен быть положительным числом (строго больше нуля), если это не так, то генерируется исключение командой:

```python
raise TypeError('вес должен быть положительным числом')
```
Данные методы следует вызывать всякий раз при записи новых значений в атрибуты _name и _weight (а также при их создании).

На основе базового класса Furniture объявить следующие дочерние классы:


* Closet - для представления шкафов;
* Chair - для представления стульев;
* Table - для представления столов.

Объекты этих классов должны создаваться командами:

* obj = Closet(name, weight, tp, doors)   # tp: True - шкаф-купе; False - обычный шкаф; doors - число дверей (целое число)
* obj = Chair(name, weight, height)       # height - высота стула (любое положительное число)
* obj = Table(name, weight, height, square) # height - высота стола; square - площадь поверхности (любые положительные числа)
В каждом объекте этих классов должны создаваться соответствующие защищенные атрибуты:

* в объектах класса Closet: _name, _weight, _tp, _doors
* в объектах класса Chair: _name, _weight, _height
* в объектах класса Table: _name, _weight, _height, _square

В каждом классе (`Closet, Chair, Table`) объявить метод:

```python
get_attrs()
```
который возвращает кортеж из значений локальных защищенных атрибутов объектов этих классов.

Пример использования классов :

```python
cl = Closet('шкаф-купе', 342.56, True, 3)
chair = Chair('стул', 14, 55.6)
tb = Table('стол', 34.5, 75, 10)
print(tb.get_attrs())
```